const AWS = require('aws-sdk');
const { marketURI, channelURI, blockchainURI } = require('../constants/uri');

const lambdaDispatcher = new AWS.Lambda({ region: 'us-east-1' });

const [SCHEDULER, CHANNEL, BLOCKCHAIN] = [
  'decentralized-exchange-scheduler',
  'decentralized-exchange-channel',
  'decentralized-exchange-user',
];
const [GET, POST] = ['GET', 'POST'];

/**
 * lambda invoke proxy
 */
class LambdaProxy {
  /**
   * invoke lambda
   * @param {String} functionName
   * @param {String} httpMethod
   * @param {String} path
   * @param {Any} body
   * @param {Boolean} async
   * @return {Promise<Any>} response body
   */
  static invoke(functionName, httpMethod, path, body, async = false) {
    return new Promise((resolve, reject) => {
      const event = {
        httpMethod,
        path,
        body: JSON.stringify(body),
        headers: { 'content-type': 'application/json' },
      };

      console.info('invoke lambda event:', event);

      if (async) {
        lambdaDispatcher.invokeAsync(
          { FunctionName: functionName, InvokeArgs: JSON.stringify(event) },
          (err, data) => {
            try {
              if (err) {
                console.info('async invoke lambda error:', err);
                reject(err);
              } else {
                console.info('async invoke lambda success:', data);
                resolve(data);
              }
            } catch (e) {
              reject(e);
            }
          }
        );
      } else {
        lambdaDispatcher.invoke(
          { FunctionName: functionName, Payload: JSON.stringify(event) },
          (err, data) => {
            try {
              if (err) {
                console.info('invoke lambda error:', err);
                reject(err);
              } else {
                console.info('invoke lambda success:', data);
                resolve(JSON.parse(JSON.parse(data.Payload).body));
              }
            } catch (e) {
              reject(e);
            }
          }
        );
      }
    });
  }

  /**
   * listPrice
   * @param {String} coinPair
   * @param {Integer} minutes
   * @param {Integer} from
   * @param {Integer} to
   * @return {Promise<Decimal>}
   */
  static listPrice({ coinPair, minutes, from, to }) {
    return LambdaProxy.invoke(SCHEDULER, POST, marketURI.listPrice, {
      coinPair,
      minutes,
      from,
      to,
    });
  }

  /**
   * listKLine
   * @param {String} coinPair
   * @param {Integer} minutes
   * @param {Integer} from
   * @param {Integer} to
   * @return {Promise<Decimal>}
   */
  static listKLine(coinPair, minutes, from, to) {
    return LambdaProxy.invoke(SCHEDULER, POST, marketURI.listKLine, {
      coinPair,
      minutes,
      from,
      to,
    });
  }

  /**
   * sendEmail
   * @param {String} subject
   * @param {String} to
   * @param {String} content
   * @return {Promise<Any>}
   */
  static sendEmail(subject, to, content) {
    return LambdaProxy.invoke(CHANNEL, POST, channelURI.sendEmail, { subject, to, content }, true);
  }

  /**
   * sendSMS
   * @param {String} to
   * @param {String} content
   * @return {Promise<Any>}
   */
  static sendSMS(to, content) {
    return LambdaProxy.invoke(CHANNEL, POST, channelURI.sendSMS, { to, content }, true);
  }

  /**
   * fetchBlockchainTxHash
   * @param {Integer} transId
   * @param {String} type
   * @return {Promise<Any>}
   */
  static fetchBlockchainTxHash(transId, type) {
    return LambdaProxy.invoke(BLOCKCHAIN, POST, blockchainURI.txHash, { transId, type });
  }

  /**
   * @param {String} address
   * @param {String} type
   * @return {Promise<Any>}
   */
  static getCoinBalance(address, type) {
    return LambdaProxy.invoke(BLOCKCHAIN, POST, blockchainURI.getBalance, { address, type });
  }
}

module.exports = LambdaProxy;
